﻿// SyntheticDataGenerator.Core.Taxonomy
//
// (c) 2011 Arthur Pitman
//
// Part of Market-Basket Synthetic Data Generator
// A C# adaptation of the IBM Quest Market-Basket Synthetic Data Generator
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// (Version 2) as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License (Version 2) along with this program; if not, write to 
// the Free Software Foundation, Inc., 
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// or see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SyntheticDataGenerator.Generators;
using System.IO;

namespace SyntheticDataGenerator.Core
{
	/// <summary>
	/// A taxonomy
	/// </summary>
	public class Taxonomy
	{
		/// <summary>
		/// Number of items
		/// </summary>
		public int ItemCount { get; set; }

		/// <summary>
		/// Number of roots
		/// </summary>
		public int RootCount { get; set; }

		/// <summary>
		/// Used to assign probabilities to items?
		/// </summary>
		public double DepthRatio { get; set; }

		/// <summary>
		/// Specifies the index of the parent of a node, -1 is no parent
		/// </summary>
		public int[] Structure { get; set; }

		/// <summary>
		/// 
		/// </summary>
		protected int[] childStart;

		/// <summary>
		/// 
		/// </summary>
		protected int[] childEnd;
 
		/// <summary>
		/// 
		/// </summary>
		/// <param name="itemCount"></param>
		/// <param name="rootCount"></param>
		/// <param name="fanout"></param>
		/// <param name="depthRatio"></param>
		public Taxonomy(int itemCount, int rootCount, double fanout, double depthRatio)
		{
			ItemCount = itemCount;
			RootCount = rootCount;
			DepthRatio = depthRatio;
			Structure = new int[ItemCount];
			childStart = new int[ItemCount];
			childEnd = new int[ItemCount];
		
			var childCountGenerator = new PoissonGenerator(fanout - 1);	// string length			

			// initialize parents (or lack thereof) for roots
			int j;
			for (j = 0; j < RootCount; j++)
				Structure[j] = -1;
			
			// set up all the interior nodes			
			int i;
			int nextChild = RootCount;
			for ( i = 0; i < ItemCount && nextChild < ItemCount; i++)
			{
				childStart[i] = nextChild;
				nextChild += childCountGenerator.NextInt() + 1;
				if (nextChild > ItemCount)
					nextChild = ItemCount;
				childEnd[i] = nextChild;
				for (; j < nextChild; j++)
					Structure[j] = i;
			}

			// initialize children (or lack thereof) for all the leaves
			for (; i < ItemCount; i++)
				childStart[i] = childEnd[i] = -1;
		}

		/// <summary>
		/// Checks if an item is the root item
		/// </summary>
		/// <param name="item"></param>
		/// <returns>Returns <c>true</c> if the item is the root item</returns>
		public bool IsRoot(int item)
		{
			return (Structure[item] == -1);
		}

		/// <summary>
		/// Gets the child count for an item
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public int GetChildCount(int item)
		{
			return childEnd[item] - childStart[item];
		}

		/// <summary>
		/// Gets the n'th child of an item 
		/// </summary>
		/// <param name="item"></param>
		/// <param name="n"></param>
		/// <returns></returns>
		public int GetChild(int item, int n)
		{
			return childStart[item] + n;
		}

		/// <summary>
		/// Gets the first child for an item
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public int GetFirstChild(int item)
		{
			return childStart[item];
		}

		/// <summary>
		/// Gets the last child for an item
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public int GetLastChild(int item)
		{
			return childEnd[item] - 1;
		}

		/// <summary>
		/// Writes the <see cref="Taxonomy"/> to the specified <see cref="TextWriter"/>
		/// </summary>
		/// <param name="writer"></param>
		public void Write(TextWriter writer)
		{
			for (int i = 0; i < ItemCount; i++)
			{
				if (Structure[i] >= 0)            
					writer.WriteLine("{0} {1}", i, Structure[i]);
			}
		}     
	}
}
